package com.grechur.wanandroidcompose.widget.banner


import android.util.Log
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChangeConsumed
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.calculateCurrentOffsetForPage
import com.grechur.wanandroidcompose.widget.banner.bean.BaseBannerBean
import com.grechur.wanandroidcompose.widget.banner.config.BannerConfig
import com.grechur.wanandroidcompose.widget.banner.util.HorizontalPagerIndicator
import com.grechur.wanandroidcompose.widget.banner.util.VerticalPagerIndicator
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.delay
import kotlin.math.absoluteValue

private const val TAG = "BannerPager"

/**
 * 新增一个 Banner，最简单的情况下只需传入数据即可，如果需要更多样式请查看下面参数。 这里需要注意的是，数据 Model 必须要继承自 BaseBannerBean，因为在 BannerCard 中需要使用到其中的一些参数
Params:
items - 数据
config - Banner 的一些配置参数 BannerConfig
indicatorIsVertical - 指示器是否为竖向
indicatorGravity - Banner 指示器位置，直接使用 Alignment 即可进行设定
onBannerClick - Banner 点击事件的回调
 */
@OptIn(ExperimentalPagerApi::class)
@Composable
fun <T : BaseBannerBean> BannerPagerView(
    modifier: Modifier = Modifier,
    items: List<T> = arrayListOf(),
    config: BannerConfig = BannerConfig(),
    indicatorIsVertical: Boolean = false,
    indicatorGravity: Alignment = Alignment.BottomCenter,
    onBannerClick: (T) -> Unit,
) {
    if (items.isEmpty()) {
        throw NullPointerException("items is not null")
    }
    val size = items.size
    val loopingCount = Int.MAX_VALUE
    var startIndex = loopingCount / 2
    startIndex -= startIndex % size
    val pagerState = rememberPagerState()
    var executeChangePage by remember { mutableStateOf(false) }
    var currentPageIndex = 0
    fun pageMapper(index: Int): Int {
        return (index - startIndex).floorMod(size)
    }

    Box(modifier = modifier) {
        HorizontalPager(
            modifier = Modifier.pointerInput(pagerState.currentPage) {
                awaitPointerEventScope {
                    while (true) {
                        val event = awaitPointerEvent(PointerEventPass.Initial)
                        val dragEvent = event.changes.firstOrNull()
                        when {
                            dragEvent!!.positionChangeConsumed() -> {
                                return@awaitPointerEventScope
                            }

                            dragEvent.changedToDownIgnoreConsumed() -> {
                                //记录下当前的页面索引值
                                currentPageIndex = pagerState.currentPage
                            }

                            dragEvent.changedToUpIgnoreConsumed() -> {
                                if (pagerState.targetPage == null) return@awaitPointerEventScope
                                if (currentPageIndex == pagerState.currentPage && pagerState.pageCount > 1) {
                                    executeChangePage = !executeChangePage
                                }
                            }
                        }
                    }
                }
            },
            state = pagerState,
            count = loopingCount
        ) { page ->
            val p = page % size
            val item = items[p]

            BannerCard(
                bean = item,
                modifier = Modifier
                    .graphicsLayer {
                        val offset = calculateCurrentOffsetForPage(page).absoluteValue
                        lerp(
                            start = 0.75f,
                            stop = 1f,
                            fraction = 1f-offset.coerceIn(0f,1f)
                        ).also { scale->
                            scaleX = scale
                            scaleY = scale
                        }
                        alpha = lerp(
                            start = 0.5f,
                            stop = 1f,
                            fraction = 1f - offset.coerceIn(0f, 1f)
                        )
                    }
                    .padding(config.bannerImagePadding),
                shape = config.shape,
                imageRatio = config.imageRatio,
                contentScale = config.contentScale
            ) {
                Log.d(TAG, "item is :${item.javaClass}")
                onBannerClick(item)
            }
        }

        if (config.repeat) {
            LaunchedEffect(key1 = executeChangePage) {
                try {
//                    while (true) {
//                        delay(config.intervalTime)
//                        val current = pagerState.currentPage
//                        val currentPos = pageMapper(current)
//                        val nextPage = current + 1
//                        val toPage = nextPage.takeIf { nextPage < pagerState.pageCount }
//                            ?: (currentPos + startIndex + 1)
//                        if (toPage > current) {
//                            pagerState.animateScrollToPage(toPage)
//                        } else {
//                            pagerState.scrollToPage(toPage)
//                        }
//                    }
                    val current = pagerState.currentPage
                    val currentPos = pageMapper(current)
                    val nextPage = current + 1
                    val toPage = nextPage.takeIf { nextPage < pagerState.pageCount }
                        ?: (currentPos + startIndex + 1)
                    executeChangePage = if (toPage > current) {
                        delay(timeMillis = config.intervalTime)
                        pagerState.animateScrollToPage(toPage)
                        !executeChangePage
                    } else {
                        delay(timeMillis = config.intervalTime)
                        pagerState.scrollToPage(toPage)
                        !executeChangePage
                    }
                } catch (e: Exception) {
                    Log.i("page", "Launched paging cancelled")
                }
            }
        }

        if (indicatorIsVertical) {
            VerticalPagerIndicator(
                pagerState = pagerState,
                pageCount = size,
                modifier = Modifier
                    .align(indicatorGravity)
                    .padding(16.dp),
            )
        } else {
            HorizontalPagerIndicator(
                pagerState = pagerState,
                pageCount = size,
                modifier = Modifier
                    .align(indicatorGravity)
                    .padding(16.dp),
            )
        }

    }
}

private fun Int.floorMod(other: Int): Int = when (other) {
    0 -> this
    else -> this - floorDiv(other) * other
}
